/**
 * Copyright (c) 2009 Netflix-Flex_API Contributors.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of 
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to 
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
 * of the Software, and to permit persons to whom the Software is furnished to do 
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 * */
package com.netflix.webapis.xml
{
	import com.netflix.webapis.ServiceStorage;
	import com.netflix.webapis.catalog.model.CatalogItemModelBase;
	import com.netflix.webapis.catalog.model.FilmographyItemModel;
	import com.netflix.webapis.catalog.model.MovieCatalogItemModel;
	import com.netflix.webapis.catalog.model.SeasonCatalogItemModel;
	import com.netflix.webapis.catalog.vo.AwardNomineeVO;
	import com.netflix.webapis.catalog.vo.CatalogTitleVO;
	import com.netflix.webapis.catalog.vo.CategoryItemVO;
	import com.netflix.webapis.catalog.vo.FormatAvailabilityVO;
	import com.netflix.webapis.catalog.vo.LangaugeFormatVO;
	import com.netflix.webapis.catalog.vo.LinkItemVO;
	import com.netflix.webapis.catalog.vo.PersonVO;
	import com.netflix.webapis.catalog.vo.ScreenFormatVO;
	import com.netflix.webapis.catalog.vo.TitleFormatVO;
	import com.netflix.webapis.users.vo.QueueItemVO;
	
	/**
	 * Utility file to parse XML results from Netflix API.
	 * @author brianburck
	 * 
	 */
	public class NetflixXMLUtil
	{
		public static const ID:String = "id";
		public static const CATALOG_TITLE:String = "catalog_title";
		public static const TITLE:String = "title";
		public static const BOX_ART:String = "box_art";
		public static const LINK : String = "link";
		public static const RELEASE_YEAR:String = "release_year";
		public static const CATEGORY:String = "category";
		public static const RUNTIME:String = "runtime";
		public static const AVERAGE_RATING:String = "average_rating";
		
		public static const PERSON:String = "person";
		public static const NAME:String = "name";
		public static const BIO:String = "bio";
		
		public static const SYNOPSIS_ATTR:String = "synopsis";
		public static const CAST_ATTR:String = "cast";
		public static const DIRECTOR_ATTR:String = "directors";
		public static const FORMATS_ATTR:String = "formats";
		public static const SCREEN_FORMATS_ATTR:String = "screen formats";
		public static const AUDIO_ATTR:String = "languages and audio";
		public static const SEASONS_ATTR:String = "seasons";
		public static const EPISODES_ATTR:String = "episodes";
		public static const SIMILARS_ATTR:String = "similars";
		public static const OFFICIAL_SITE_ATTR:String = "official webpage";
		public static const NETFLIX_PAGE_ATTR:String = "web page";
		public static const FILMOGRAPHY_ATTR:String = "filmography";
		public static const DISCS_ATTR:String = "discs";
		
		public static const DISCS_SEPARATOR:String = "discs";
		public static const SEASONS_SEPARATOR:String = "seasons";
		public static const EPISODES_SEPARATOR:String = "episodes";
		public static const SERIES_SEPARATOR:String = "series";
		public static const MOVIES_SEPARATOR:String = "movies";
		public static const CATALOG_SEPARATOR:String = "catalog";
		
		public static const POSITION_NODE:String = "position";
		public static const UPDATED_NODE:String = "updated";
		
		public static const ETAG_NODE:String = "etag";
		
		
		public static function handleXMLToCatalogItemModel(xml:XML,model:CatalogItemModelBase=null):CatalogItemModelBase
		{
			if(!model)
				model = new CatalogItemModelBase();
			
			var resultNode:XML;
			model.categories = new Array;
			model.links = new Array;
			
			for each (resultNode in xml.children()) {
				var nodeType:String = String(resultNode.name());
				switch (nodeType) {
					case ID:
						model.id = handleIDNode(resultNode);
						setTitleIDsModel(model);
					break;
					case ETAG_NODE:
						handleETagNode(resultNode);
					break;
					case TITLE:
						model.titleShort = resultNode.@short;
						model.titleRegular = resultNode.@regular;
					break;
					case BOX_ART:
						model.boxArtSmall = resultNode.@small;
						model.boxArtMedium = resultNode.@medium;
						model.boxArtLarge = resultNode.@large;
					break;
					case LINK:
						var linkTitle:String = resultNode.@title;
						switch (linkTitle) {
							case SYNOPSIS_ATTR:
								model.synopsis = handleLink(resultNode);
							break;
							case CAST_ATTR:
								model.cast = handleLink(resultNode);
							break;
							case DIRECTOR_ATTR:
								model.directors = handleLink(resultNode);
							break;
							case FORMATS_ATTR:
								model.formats = handleLink(resultNode);
							break;
							case SCREEN_FORMATS_ATTR:
								model.screenFormats = handleLink(resultNode);
							break;
							case AUDIO_ATTR:
								model.languagesAndAudio = handleLink(resultNode);
							break;
							case SEASONS_ATTR:
								model.seasons = handleLink(resultNode);
							break;
							case EPISODES_ATTR:
								model.episodes = handleLink(resultNode);
							break;
							case SIMILARS_ATTR:
								model.similars = handleLink(resultNode);
							break;
							case OFFICIAL_SITE_ATTR:
								model.officialSite = handleLink(resultNode);
							break;
							case NETFLIX_PAGE_ATTR:
								model.webPage = handleLink(resultNode);
							break;
							case DISCS_ATTR:
								model.discs = handleLink(resultNode);
							break;
							default:
								model.links.push(handleLink(resultNode));
							break;
						}
						
					break;
					case RELEASE_YEAR:
						model.releaseYear = handleReleaseYear(resultNode);
					break;
					case RUNTIME:
						model.runtime = handleRuntime(resultNode);
					break;
					case CATEGORY:
						model.categories.push(handleCategory(resultNode));
					break;
					case AVERAGE_RATING:
						model.averageRating = handleAvgRating(resultNode);
					break;
				}
			}

			return model;
			
		}
		
		/**
		 * Handles the parsing of a <code>catalog_title</code> node. 
		 * @param xml <code>catalog_title</code> node
		 * @return CatalogTitleVO
		 * 
		 */		
		public static function handleCatalogTitleNode(xml:XML, movieItemVO:CatalogTitleVO):CatalogTitleVO {
			var resultNode:XML;
			movieItemVO.categories = new Array;
			movieItemVO.links = new Array;
			
			for each (resultNode in xml.children()) {
				var nodeType:String = String(resultNode.name());
				switch (nodeType) {
					case ID:
						movieItemVO.id = handleIDNode(resultNode);
						setTitleIDs(movieItemVO);
					break;
					case TITLE:
						movieItemVO.titleShort = resultNode.@short;
						movieItemVO.titleRegular = resultNode.@regular;
					break;
					case BOX_ART:
						movieItemVO.boxArtSmall = resultNode.@small;
						movieItemVO.boxArtMedium = resultNode.@medium;
						movieItemVO.boxArtLarge = resultNode.@large;
					break;
					case LINK:
						var linkTitle:String = resultNode.@title;
						switch (linkTitle) {
							case SYNOPSIS_ATTR:
								movieItemVO.synopsis = handleLink(resultNode);
							break;
							case CAST_ATTR:
								movieItemVO.cast = handleLink(resultNode);
							break;
							case DIRECTOR_ATTR:
								movieItemVO.directors = handleLink(resultNode);
							break;
							case FORMATS_ATTR:
								movieItemVO.formats = handleLink(resultNode);
							break;
							case SCREEN_FORMATS_ATTR:
								movieItemVO.screenFormats = handleLink(resultNode);
							break;
							case AUDIO_ATTR:
								movieItemVO.languagesAndAudio = handleLink(resultNode);
							break;
							case SEASONS_ATTR:
								movieItemVO.seasons = handleLink(resultNode);
							break;
							case EPISODES_ATTR:
								movieItemVO.episodes = handleLink(resultNode);
							break;
							case SIMILARS_ATTR:
								movieItemVO.similars = handleLink(resultNode);
							break;
							case OFFICIAL_SITE_ATTR:
								movieItemVO.officialSite = handleLink(resultNode);
							break;
							case NETFLIX_PAGE_ATTR:
								movieItemVO.webPage = handleLink(resultNode);
							break;
							case DISCS_ATTR:
								movieItemVO.discs = handleLink(resultNode);
							break;
							default:
								movieItemVO.links.push(handleLink(resultNode));
							break;
						}
						
					break;
					case RELEASE_YEAR:
						movieItemVO.releaseYear = handleReleaseYear(resultNode);
					break;
					case RUNTIME:
						movieItemVO.runtime = handleRuntime(resultNode);
					break;
					case CATEGORY:
						movieItemVO.categories.push(handleCategory(resultNode));
					break;
					case AVERAGE_RATING:
						movieItemVO.averageRating = handleAvgRating(resultNode);
					break;
					case POSITION_NODE:
						if (movieItemVO is QueueItemVO) {
							(movieItemVO as QueueItemVO).queuePosition = handlePosition(resultNode);
						}
					break;
					case UPDATED_NODE:
						if (movieItemVO is QueueItemVO) {
							(movieItemVO as QueueItemVO).lastUpdated = handleUpdatedDate(resultNode);
						}
					break;
				}
			}

			return movieItemVO;
		}
		
		/**
		 * Handles the parsing of a <code>person</code> node. 
		 * @param xml <code>person</code> node
		 * @return PersonVO
		 * 
		 */		
		public static function handlePerson(xml:XML):PersonVO {
			var resultNode:XML;
			var personVO:PersonVO = new PersonVO;
			personVO.links = new Array;
			
			for each (resultNode in xml.children()) {
				var nodeType:String = String(resultNode.name());
				switch (nodeType) {
					case LINK:
						var linkType:String = resultNode.@title;
						switch (linkType) {
							case FILMOGRAPHY_ATTR:
								personVO.filmography = handleLink(resultNode);
							break;
							case NETFLIX_PAGE_ATTR:
								personVO.webPage = handleLink(resultNode);
							break;
							default:
								personVO.links.push(handleLink(resultNode));
							break;
						}
					break;
					case BIO:
						personVO.bio = handleBio(resultNode);
					break;
					case NAME:
						personVO.name = handleName(resultNode);
					break;
					case ID:
						personVO.id = handleIDNode(resultNode);
					break;
				}
			}
			return personVO
		}
		
		/**
		 * Handles the ID node parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleIDNode(xml:XML):String {
			return xml.valueOf().toString();
		}
		
		/**
		 * Handles the Release Year node parsing.
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleReleaseYear(xml:XML):Number {
			return Number(xml.valueOf());
		}
		
		/**
		 * Handles the Runtime node parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleRuntime(xml:XML):Number {
			return Number(xml.valueOf());
		}
		
		/**
		 * Handles the Average Rating node parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleAvgRating(xml:XML):Number {
			return Number(xml.valueOf());
		}
		
		/**
		 * Handles the Link node parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleLink(xml:XML):LinkItemVO {
			var linkVO:LinkItemVO = new LinkItemVO;
			linkVO.url = xml.@href;
			linkVO.rel = xml.@rel;
			linkVO.title = xml.@title;
			if (xml.children().length() > 0) {
				linkVO.expansion = new Array;
				var childXML:XML = xml.children()[0] as XML;
				linkVO.expansionTitle = String(childXML.name());
				for each( var linkXML:XML in childXML.children()) {
					if (linkXML.name() == LINK)
						linkVO.expansion.push(handleLink(linkXML));
				}
			}
			return linkVO;
		}
		
		/**
		 * Handles the Category node parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleCategory(xml:XML):CategoryItemVO{
			var category:CategoryItemVO = new CategoryItemVO;
			category.scheme = xml.@scheme;
			category.label = xml.@label;
			category.term = xml.@term;
			return category;
		}
		
		/**
		 * Handles the Bio node parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleBio(xml:XML):String {
			return xml.valueOf().toString();
		}
		
		/**
		 * Handles the Name node parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleName(xml:XML):String {
			return xml.valueOf().toString();
		}
		/**
		 * Handle position parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handlePosition(xml:XML):Number {
			return Number(xml.valueOf().toString());
		}
		/**
		 * Handle Updated Date parsing. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleUpdatedDate(xml:XML):Date {
			return new Date(Number(xml.valueOf().toString()));
		}
		
		/**
		 * Handles the Title Id node parsing. 
		 * @param title
		 * 
		 */		
		public static function setTitleIDsModel(model:CatalogItemModelBase):void {
			var urlArray:Array = model.id.split("/");
			var lastID:Number = NaN;
			for (var index:Number = urlArray.length - 1; index > 0; index--) {
				if (isNaN(lastID)) {
					if (Number(urlArray[index])){
						lastID = Number(urlArray[index]);
					} else {
						continue;
					}
				}  else {
					var identifier:String = String(urlArray[index]);
					switch (identifier) {
						/* case DISCS_SEPARATOR:
							model.discID = lastID;
							model.titleType = CatalogTitleVO.DISC;
						break;
						case EPISODES_SEPARATOR:
							model.episodeID = lastID;
							model.titleType = CatalogTitleVO.EPISODE;
						break; */
						case MOVIES_SEPARATOR:
							MovieCatalogItemModel(model).movieID = lastID;
							model.titleType = CatalogTitleVO.MOVIE;
						break;
						case SEASONS_SEPARATOR:
							SeasonCatalogItemModel(model).seasonID = lastID;
							model.titleType = CatalogTitleVO.SEASON;
						break;
						case SERIES_SEPARATOR:
							SeasonCatalogItemModel(model).seriesID = lastID;
							if (model.titleType == null)
								model.titleType = CatalogTitleVO.SERIES;
						break;
					}
					
					lastID = NaN;
				}
			}
		}
		/**
		 * Handles the Title Id node parsing. 
		 * @param title
		 * 
		 */		
		public static function setTitleIDs(title:CatalogTitleVO):void {
			var urlArray:Array = title.id.split("/");
			var lastID:Number = NaN;
			for (var index:Number = urlArray.length - 1; index > 0; index--) {
				if (isNaN(lastID)) {
					if (Number(urlArray[index])){
						lastID = Number(urlArray[index]);
					} else {
						continue;
					}
				}  else {
					var identifier:String = String(urlArray[index]);
					switch (identifier) {
						case DISCS_SEPARATOR:
							title.discID = lastID;
							title.titleType = CatalogTitleVO.DISC;
						break;
						case EPISODES_SEPARATOR:
							title.episodeID = lastID;
							title.titleType = CatalogTitleVO.EPISODE;
						break;
						case MOVIES_SEPARATOR:
							title.movieID = lastID;
							title.titleType = CatalogTitleVO.MOVIE;
						break;
						case SEASONS_SEPARATOR:
							title.seasonID = lastID;
							title.titleType = CatalogTitleVO.SEASON;
						break;
						case SERIES_SEPARATOR:
							title.seriesID = lastID;
							if (title.titleType == null)
								title.titleType = CatalogTitleVO.SERIES;
						break;
					}
					
					lastID = NaN;
				}
			}
		}
		/**
		 * Handles the parsing of Format Availability Results. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleFormatAvailability(xml:XML):FormatAvailabilityVO
		{
			var availability:FormatAvailabilityVO = new FormatAvailabilityVO();
			availability.label = xml.category.@label;
			availability.term = xml.category.@term;
			availability.availableFromAvailable = (xml.@available_from!=undefined)?true:false;
			availability.availableUntilAvailable = (xml.@available_until!=undefined)?true:false;
			availability.availableUntil = (xml.@available_until)?new Date(Number(xml.@available_until)*1000):null;
			availability.availableFrom = (xml.@available_from)?new Date(Number(xml.@available_from)*1000):null;
			return availability;
		}
		/**
		 * Handles the parsing of Awards Results. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleAwards(xml:XML):AwardNomineeVO
		{
			var award:AwardNomineeVO = new AwardNomineeVO();
			award.year = xml.@year;
			award.category = new CategoryItemVO();
			award.category.scheme = xml.category.@scheme;
			award.category.label = xml.category.@label;
			award.category.term = xml.category.@term;
			award.link = new LinkItemVO();
			award.link.url = xml.link.@href;
			award.link.rel = xml.link.@rel;
			award.link.title = xml.link.@title;
			return award;
		}
		/**
		 * Handles the parsing of Screen Formats Results.  
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleScreenFormat(xml:XML):ScreenFormatVO
		{
			var screenFormat:ScreenFormatVO = new ScreenFormatVO();
			screenFormat.categories = new Array();
			for each(var x:XML in xml..category){
				var category:CategoryItemVO = new CategoryItemVO();
				category.label = x.@label;
				category.scheme = x.@scheme;
				category.term = x.@term;
				screenFormat.categories.push(category);
			}
			return screenFormat;
		}
		
		/**
		 * Handles the parsing of Language and Audio Formats. 
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleLanguageAudioFormat(xml:XML):TitleFormatVO
		{
			var title:TitleFormatVO = new TitleFormatVO();
			title.format = new CategoryItemVO();
			title.format.label = xml.category.@label;
			title.format.scheme = xml.category.@scheme;
			title.format.term = xml.category.@term;
			title.languages = new Array();
			for each(var x:XML in xml.category){
				var language:LangaugeFormatVO = new LangaugeFormatVO();
				language.language = new CategoryItemVO();
				language.language.label = x.@label;
				language.language.scheme = x.@scheme;
				language.language.term = x.@term;
				language.audioFormats = new Array();
				for each(var y:XML in y.category){
					var audio:CategoryItemVO = new CategoryItemVO();
					audio.label = y.@label;
					audio.scheme = y.@scheme;
					audio.term = y.@term;
					language.audioFormats.push(audio);
				}
				title.languages.push(language);
			}
			return title;
		}
		
		/**
		 * Handles the parsing of Filmography Results.
		 * @param xml
		 * @return 
		 * 
		 */		
		public static function handleFilmography(xml:XML):FilmographyItemModel
		{
			return NetflixXMLUtil.handleXMLToCatalogItemModel(xml) as FilmographyItemModel;
		}
		
		public static function handleETagNode(xml:XML):void {
			ServiceStorage.getInstance().lastQueueETag = xml.valueOf().toString();
		}
		
	}
}